package com.dlc.shop.search.common.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dlc.shop.bean.bo.BrandLangBO;
import com.dlc.shop.bean.bo.CategoryLangBO;
import com.dlc.shop.bean.enums.ProdStatusEnums;
import com.dlc.shop.bean.enums.ProdType;
import com.dlc.shop.bean.model.Category;
import com.dlc.shop.bean.model.ProdLang;
import com.dlc.shop.bean.param.EsProductParam;
import com.dlc.shop.bean.param.ProductExportParam;
import com.dlc.shop.bean.vo.search.*;
import com.dlc.shop.common.bean.LangConfig;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.enums.StatusEnum;
import com.dlc.shop.common.i18n.I18nMessage;
import com.dlc.shop.common.properties.EsProperties;
import com.dlc.shop.common.util.EsPageVO;
import com.dlc.shop.common.util.PageParam;
import com.dlc.shop.common.util.PoiExcelUtil;
import com.dlc.shop.config.ShopConfig;
import com.dlc.shop.manager.impl.LangManager;
import com.dlc.shop.search.common.constant.EsConstant;
import com.dlc.shop.search.common.constant.EsIndexEnum;
import com.dlc.shop.search.common.constant.EsProductSortEnum;
import com.dlc.shop.search.common.dao.EsProductMapper;
import com.dlc.shop.search.common.param.EsPageParam;
import com.dlc.shop.search.common.service.SearchProductService;
import com.dlc.shop.search.common.util.EsSearchUtil;
import com.dlc.shop.search.common.util.SearchResponseUtil;
import com.dlc.shop.service.*;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import java.io.IOException;
import java.io.StringReader;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Yami
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SearchProductServiceImpl implements SearchProductService {

    private final EsProperties esProperties;

    private final ShopConfig shopConfig;

    private final BrandService brandService;
    private final CategoryService categoryService;
    private final ProdLangService prodLangService;
    private final ProductExcelService productExcelService;
    private final EsProductMapper esProductMapper;
    private final LangManager langManager;

    /**
     * 关键字数量
     */
    private static final Integer KEYWORD_SIZE = 3;

    /**
     * 通过搜索信息分页搜索es数据并聚合返回的信息
     * @param pageParam 分页数据
     * @param productParam 商品搜索条件
     * @return 搜索结果
     */
    @Override
    public EsPageVO<EsProductSearchVO> page(EsPageParam pageParam, EsProductParam productParam, Boolean isAgg) {
        if (Objects.isNull(productParam.getAppDisplay())) {
            productParam.setAppDisplay(Boolean.TRUE);
        }
        if (esProperties.getEnable()) {
            SearchResponse response = pageSearchResult(pageParam, productParam, isAgg);
            return buildSearchResult(pageParam,response);
        } else {
            EsPageVO<ProductSearchVO> page = this.basePage(pageParam, productParam, isAgg);
            return this.buildSearchResult(page, isAgg);
        }
    }


    /**
     * 获取搜索扩展信息
     * @param pageParam 分页数据
     * @param productParam 商品搜索条件
     * @return 搜索结果
     */
    @Override
    public EsProductSearchVO searchExtension(EsPageParam pageParam, EsProductParam productParam) {
        EsProductParam newEsProductParam = new EsProductParam();
        newEsProductParam.setKeyword(productParam.getKeyword());
        newEsProductParam.setPrimaryCategoryId(productParam.getPrimaryCategoryId());
        newEsProductParam.setSecondaryCategoryId(productParam.getSecondaryCategoryId());
        newEsProductParam.setShopCategoryId(productParam.getShopCategoryId());
        pageParam.setSize(0);
        SearchResponse response = pageSearchResult(pageParam, newEsProductParam, Boolean.TRUE);
        EsProductSearchVO productSearch = SearchResponseUtil.getProductSearch(response);
        getProdLang(productSearch);
        return productSearch;
    }

    /**
     * 根据spuId列表，获取spu信息
     * @param productParam  商品搜索条件
     * @return spu列表
     */
    @Override
    public List<ProductSearchVO> listSpuByProdIds(EsProductParam productParam) {
        EsPageParam pageParam = new EsPageParam();
        pageParam.setCurrent(1);
        pageParam.setSize(productParam.getProdIds().size());
        EsPageVO<ProductSearchVO> page = this.basePage(pageParam, productParam, Boolean.FALSE);
        List<ProductSearchVO> productSearchList = page.getRecords();
        getProdLang(productSearchList);
        return productSearchList;
    }

    @Override
    public EsPageVO<ProductSearchVO> adminPage(EsPageParam pageParam, EsProductParam productParam) {
        EsPageVO<ProductSearchVO> result = this.basePage(pageParam, productParam, Boolean.FALSE);
        Integer lang = productParam.getLang();
        if (lang == null){
            getProdLang(result.getRecords());
        }else{
            getProdLang(result.getRecords(),lang);
        }

        return result;
    }

    @Override
    public List<ProductSearchVO> simpleList(List<Long> spuIds) {
        EsProductParam productParam = new EsProductParam();
        productParam.setFetchSource(EsConstant.SIMPLE_FETCH_SOURCE);
        productParam.setProdIds(spuIds);
        productParam.setGetDelete(true);
        return listSpuByProdIds(productParam);
    }

    @Override
    public EsPageVO<ProductSearchVO> renovationPage(EsPageParam pageParam, EsProductParam productParam, Long size) {
        if (Objects.isNull(pageParam.getSize()) || pageParam.getSize() == 0) {
            EsPageVO<ProductSearchVO> pageVO = new EsPageVO<>();
            pageVO.setRecords(new ArrayList<>());
            return pageVO;
        }
        productParam.setFetchSource(EsConstant.RENOVATION_FETCH_SOURCE);
        // 不需要积分商品和活动商品
        if (Objects.isNull(productParam.getProdType())) {
            List<Integer> types = new ArrayList<>();
            types.add(ProdType.PROD_TYPE_ACTIVE.value());
            types.add(ProdType.PROD_TYPE_SCORE.value());
            productParam.setMustNotProdTypes(types);
        }

        // 如果不是搜索指定商品，就只查询可以在用户端显示的商品
        if (CollUtil.isEmpty(productParam.getProdIds())) {
            productParam.setAppDisplay(Boolean.TRUE);
        }

        EsPageVO<ProductSearchVO> searchPage =  this.adminPage(pageParam, productParam);
        long currentTime = System.currentTimeMillis();

        for (ProductSearchVO record : searchPage.getRecords()) {
            // 不是秒杀或者团购商品,或者活动还没开始的商品，就不用处理活动价格
            boolean notHandleActivityPrice = (!Objects.equals(record.getProdType(), ProdType.PROD_TYPE_GROUP.value()) &&
                    !Objects.equals(record.getProdType(), ProdType.PROD_TYPE_SECKILL.value())) ||
                    Objects.isNull(record.getActivityStartTime()) ||
                    record.getActivityStartTime() > currentTime;
            if (notHandleActivityPrice) {
                continue;
            }
            record.setOriPrice(record.getPrice());
            record.setPrice(record.getActivityPrice());
            record.setActivityPrice(null);
            record.setActivityOriginalPrice(null);
        }
        //TODO 以后优化，瀑布流装修商品重新排序
        handleProds(productParam, searchPage, size, pageParam);
        return searchPage;
    }

    @Override
    public void export(HttpServletResponse response, EsProductParam productParam) {
        productParam.setFetchSource(EsConstant.EXCEL_FETCH_SOURCE);
        productParam.setAppDisplay(Boolean.FALSE);
        LangConfig langConfig = langManager.getLangConfig();
        ExcelWriter writer = productExcelService.getProdExcelWriter(productParam.getShopId(), langConfig, false);
        try (Workbook workbook = writer.getWorkbook()) {
            Sheet sheet = writer.getSheet();
            // excel下拉数据列表组装
            productExcelService.dropDownList(productParam.getShopId(), sheet, workbook, langConfig);
            writerProdToExcel(writer, productParam, langConfig);
            PoiExcelUtil.writeExcel(response, writer);
        } catch (Exception e) {
            log.error("Exception:", e);
        }
    }

    /**
     * 瀑布流装修商品排序
     * @param productParam
     * @param searchPage
     * @param size
     */
    private void handleProds(EsProductParam productParam, EsPageVO<ProductSearchVO> searchPage, Long size, EsPageParam pageParam) {
        if(!Objects.equals(productParam.getShowSpuType(),1) || CollectionUtils.isEmpty(productParam.getProdIds())) {
            return;
        }
        if (!Objects.isNull(size)) {
            // 重新计算分页数据
            searchPage.setTotal(size);
            searchPage.setPages(Long.valueOf(size / pageParam.getSize()).intValue());
            if (size % pageParam.getSize() != 0) {
                searchPage.setPages(searchPage.getPages() + 1);
            }
        }
        List<ProductSearchVO> productSearchList = new ArrayList<>();
        Map<Long, ProductSearchVO> prodMap = searchPage.getRecords().stream().collect(Collectors.toMap(ProductSearchVO::getProdId, prod -> prod));
        for (Object prodId : productParam.getProdIds()) {
            if(!prodMap.containsKey(Long.valueOf(String.valueOf(prodId)))){
                continue;
            }
            productSearchList.add(prodMap.get(Long.valueOf(String.valueOf(prodId))));
        }
        searchPage.setRecords(productSearchList);
    }

    /**
     * 写入导出的商品数据
     * @param writer
     * @param param
     * @param langConfig
     */
    private void writerProdToExcel(ExcelWriter writer, EsProductParam param, LangConfig langConfig) {
        if (esProperties.getEnable()) {
            this.writerProdToExcelByEs(writer, param, langConfig);
        } else {
            this.writerProdToExcelByDb(writer, param, langConfig);
        }
    }

    /**
     * 使用es的scroll方法来滚动查询es的数据，可以有效的解决大数据容量读取的限制
     */
    private void writerProdToExcelByEs(ExcelWriter writer, EsProductParam param, LangConfig langConfig) {
        int row = 1;
        //设置查询超时时间
        Scroll scroll = new Scroll(TimeValue.timeValueMinutes(5L));

        Integer lang = I18nMessage.getDbLang();

        SearchRequest searchRequest = buildScrollSearchRequest(param, scroll, lang);
        // 进行第一次滚动查询
        SearchResponse searchResponse = EsSearchUtil.search(searchRequest);

        // 将商品数据写入excel
        List<ProductExportParam> prodList = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            prodList.add(JSON.parseObject(hit.getSourceAsString(), ProductExportParam.class));
        }
        List<Category> platformCategoryList = productExcelService.listPlatformCategory(param.getShopId());
        Map<Long, String> categoryMap = platformCategoryList.stream().collect(Collectors.toMap(Category::getCategoryId, Category::getCategoryName));
        row = productExcelService.writerProdToExcel(prodList, writer, param.getShopId(), row, categoryMap, langConfig);

        SearchHits hits= searchResponse.getHits();
        /**
         *在这个位置已经读到了前一千条数据，可以在这先对这一千数据进行处理。下面滚动查询剩下的数据
         */
        //记录要滚动的ID
        String scrollId = searchResponse.getScrollId();
        //滚动查询部分，将从第1001笔数据开始取
        SearchHit[] hitsScroll = hits.getHits();
        while (hitsScroll != null && hitsScroll.length > 0 ) {
            //构造滚动查询条件
            SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
            searchScrollRequest.scroll(scroll);
            searchResponse = EsSearchUtil.scroll(searchScrollRequest);
            scrollId = searchResponse.getScrollId();
            hits = searchResponse.getHits();
            hitsScroll = hits.getHits();

            // 将商品数据写入excel
            prodList.clear();
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                prodList.add(JSON.parseObject(hit.getSourceAsString(), ProductExportParam.class));
            }
            // 将查询的商品数据插入excel中
            row = productExcelService.writerProdToExcel(prodList, writer, param.getShopId(), row, categoryMap, langConfig);
        }
        //清除滚动，否则影响下次查询
        EsSearchUtil.clearScroll(scrollId);
    }

    /**
     * 写入商品数据到Excel（通过数据库）
     */
    private void writerProdToExcelByDb(ExcelWriter writer, EsProductParam productParam, LangConfig langConfig) {
        // 数据准备
        List<Category> platformCategoryList = productExcelService.listPlatformCategory(productParam.getShopId());
        Map<Long, String> categoryMap = platformCategoryList.stream().collect(Collectors.toMap(Category::getCategoryId, Category::getCategoryName));
        EsPageParam pageParam = new EsPageParam();
        pageParam.setCurrent(0);
        pageParam.setSize(1000);
        // 循环查询并写入
        int row = 1;
        boolean isEnd;
        do {
            pageParam.setCurrent(pageParam.getCurrent() + 1);
            EsPageVO<ProductSearchVO> page = this.basePage(pageParam, productParam, false);
            if (CollectionUtils.isEmpty(page.getRecords())) {
                break;
            }
            List<ProductExportParam> exportParams = page.getRecords().stream().map(productSearchVO -> {
                ProductExportParam productExportParam = new ProductExportParam();
                BeanUtils.copyProperties(productSearchVO, productExportParam);
                return productExportParam;
            }).toList();
            // 数据写入
            row = productExcelService.writerProdToExcel(exportParams, writer, productParam.getShopId(), row, categoryMap, langConfig);
            // 判断是否退出循环
            isEnd = page.getTotal().compareTo((long)pageParam.getCurrent() * pageParam.getSize()) <= 0 ;
        } while (!isEnd);
    }

    /**
     * 构建搜索结果数据
     * @param pageParam
     * @param response
     * @return
     */
    private EsPageVO<EsProductSearchVO> buildSearchResult(EsPageParam pageParam, SearchResponse response) {
        EsPageVO<EsProductSearchVO> esPageVO = new EsPageVO<>();

        // 1、返回的所有查询到的商品
        List<EsProductSearchVO> productSearchs = new ArrayList<>();
        EsProductSearchVO productSearch = SearchResponseUtil.getProductSearch(response);
        productSearchs.add(productSearch);
        getProdLang(productSearch);
        esPageVO.setRecords(productSearchs);

        // 2、分页信息
        SearchResponseUtil.buildSearchPage(pageParam, esPageVO, response);
        return esPageVO;
    }

    private EsPageVO<EsProductSearchVO> buildSearchResult(EsPageVO<ProductSearchVO> page, Boolean isAgg) {
        EsPageVO<EsProductSearchVO> esPageVO = new EsPageVO<>();
        List<EsProductSearchVO> productSearchs = new ArrayList<>(1);
        productSearchs.add(this.getEsProdSearchVO(page.getRecords(), isAgg));
        esPageVO.setRecords(productSearchs);
        esPageVO.setTotal(page.getTotal());
        esPageVO.setPages(page.getPages());
        return esPageVO;
    }

    private void getProdLang(EsProductSearchVO productSearch) {
        getProdLang(productSearch.getProducts());
        if (CollUtil.isEmpty(productSearch.getBrands()) && CollUtil.isEmpty(productSearch.getCategories())) {
            return;
        }
        LangConfig langConfig = shopConfig.getLang();
        Integer lang = I18nMessage.getLang();
        // 插入分类名称
        if (CollUtil.isNotEmpty(productSearch.getCategories())) {
            for (CategorySearchVO category : productSearch.getCategories()) {
                if (CollUtil.isEmpty(category.getCategoryLangList())) {
                    continue;
                }
                Map<Integer, CategoryLangBO> langMap = category.getCategoryLangList().stream()
                        .filter(categoryLang -> Objects.nonNull(categoryLang.getLang()) && Objects.nonNull(categoryLang.getCategoryName()))
                        .collect(Collectors.toMap(CategoryLangBO::getLang, c -> c));

                if(MapUtil.isEmpty(langMap)){
                    continue;
                }
                CategoryLangBO categoryLangBO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));
                category.setName(categoryLangBO.getCategoryName());
                category.setCategoryLangList(null);
            }
        }
        // 插入品牌名称
        if (CollUtil.isNotEmpty(productSearch.getBrands())) {
            for (BrandSearchVO brand : productSearch.getBrands()) {
                if (CollUtil.isEmpty(brand.getBrandLangList())) {
                    continue;
                }
                Map<Integer, BrandLangBO> langMap = brand.getBrandLangList().stream()
                        .filter(brandLang -> Objects.nonNull(brandLang.getLang()) && Objects.nonNull(brandLang.getName()))
                        .collect(Collectors.toMap(BrandLangBO::getLang, b -> b));

                if(MapUtil.isEmpty(langMap)){
                    continue;
                }
                BrandLangBO brandLangBO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));
                brand.setBrandName(brandLangBO.getName());
                brand.setBrandLangList(null);
            }
        }
    }

    private void getProdLang(List<ProductSearchVO> prodList,Integer lang) {
        LangConfig langConfig = shopConfig.getLang();
        if (lang == null){
            lang = I18nMessage.getLang();
        }

        for (ProductSearchVO product : prodList) {
            if (CollUtil.isEmpty(product.getProdLangList())) {
                continue;
            }
            Map<Integer, ProductSearchLangVO> langMap = product.getProdLangList().stream().filter(prod -> Objects.nonNull(prod.getLang()) && Objects.nonNull(prod.getProdName())).collect(Collectors.toMap(ProductSearchLangVO::getLang, p -> p));
            if(langMap.size() == 0){
                product.setProdName(null);
                product.setBrief(null);
                continue;
            }
            ProductSearchLangVO productSearchLangVO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));

            product.setProdName(productSearchLangVO.getProdName());
            product.setBrief(productSearchLangVO.getBrief());
        }
    }

    private void getProdLang(List<ProductSearchVO> prodList) {
        LangConfig langConfig = shopConfig.getLang();

          Integer  lang = I18nMessage.getLang();


        for (ProductSearchVO product : prodList) {
            if (CollUtil.isEmpty(product.getProdLangList())) {
                continue;
            }
            Map<Integer, ProductSearchLangVO> langMap = product.getProdLangList().stream().filter(prod -> Objects.nonNull(prod.getLang()) && Objects.nonNull(prod.getProdName())).collect(Collectors.toMap(ProductSearchLangVO::getLang, p -> p));
            if(langMap.size() == 0){
                product.setProdName(null);
                product.setBrief(null);
                continue;
            }
            ProductSearchLangVO productSearchLangVO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));

            product.setProdName(productSearchLangVO.getProdName());
            product.setBrief(productSearchLangVO.getBrief());
        }
    }

    /**
     * 通过搜索信息分页搜索es数据的信息
     * @param pageParam 分页数据
     * @param productParam 商品搜索条件
     * @param isAgg true:聚合搜索  false:非聚合搜索  null:非聚合搜索
     * @return 搜索结果
     */
    private SearchResponse pageSearchResult(EsPageParam pageParam, EsProductParam productParam, Boolean isAgg) {
        //1、准备检索请求
        SearchRequest searchRequest = buildSearchRequest(pageParam, productParam, isAgg);
        return EsSearchUtil.search(searchRequest);
    }

    /**
     * 准备滚动检索请求
     *
     * @param param 搜索参数
     * @param lang
     * @return
     */
    private SearchRequest buildScrollSearchRequest(EsProductParam param, Scroll scroll, Integer lang) {
        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 指定返回数组中的字段
        if (ArrayUtil.isNotEmpty(param.getFetchSource())) {
            searchSourceBuilder.fetchSource(param.getFetchSource(), null);
        }

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索（商品名称、店铺名称）
        keywordSearch(param, boolQueryBuilder, lang);

        // 排序
        sort(param, searchSourceBuilder, boolQueryBuilder);

        //设置最多一次能够取出1000笔数据，从第1001笔数据开始，将开启滚动查询
        //PS:滚动查询也属于这一次查询，只不过因为一次查不完，分多次查
        searchSourceBuilder.size(1000);


        log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());

        SearchRequest searchRequest = new SearchRequest(new String[]{EsIndexEnum.PRODUCT.value()}, searchSourceBuilder);
        // 将滚动放入
        searchRequest.scroll(scroll);
        return searchRequest;
    }

    /**
     * 准备检索请求
     * @param pageParam 分页参数
     * @param param 搜索参数
     * @param isAgg true:聚合搜索  false:非聚合搜索  null:非聚合搜索
     * @return
     */
    private SearchRequest buildSearchRequest(EsPageParam pageParam,EsProductParam param, Boolean isAgg) {
        if (Objects.isNull(param.getAppDisplay())) {
            param.setAppDisplay(Boolean.FALSE);
        }
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        Integer lang = I18nMessage.getLang();

        // 指定返回数组中的字段
        if (ArrayUtil.isNotEmpty(param.getFetchSource())) {
            searchSourceBuilder.fetchSource(param.getFetchSource(), null);
        } else {
            searchSourceBuilder.fetchSource(EsConstant.APP_FETCH_SOURCE, null);
        }

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索（商品名称、店铺名称）
        keywordSearch(param, boolQueryBuilder, lang);

        // 排序
        sort(param, searchSourceBuilder, boolQueryBuilder);

        //分页
        if (Objects.nonNull(pageParam)) {
            if (pageParam.getCurrent() <= 0) {
                pageParam.setCurrent(1);
            }
            searchSourceBuilder.from((pageParam.getCurrent()-1)*pageParam.getSize());
            searchSourceBuilder.size(pageParam.getSize());
        }

        // 进行聚合分析
        agg(param, searchSourceBuilder, isAgg);

        log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());
        return new SearchRequest(new String[]{EsIndexEnum.PRODUCT.value()},searchSourceBuilder);
    }

    /**
     * 聚合分析
     */
    private void agg(EsProductParam param, SearchSourceBuilder searchSourceBuilder, Boolean isAgg) {
        // 店铺进行聚合
        if (param.getKeyword() != null && param.getKeyword().length() > 0) {
            searchSourceBuilder.aggregation(AggregationBuilders.terms(EsConstant.SHOP).field(EsConstant.SHOP_ID).size(1));
        }
        if (Objects.isNull(isAgg) || !isAgg) {
            return;
        }
        // 搜索平台商品，按照平台分类信息进行聚合
        if (Objects.isNull(param.getShopId()) && Objects.isNull(param.getCategoryId())) {
            searchSourceBuilder.aggregation(EsSearchUtil.nestedAggregation(EsConstant.CATEGORY, EsConstant.CATEGORY_UNION_ID, EsConstant.CATEGORY_ID, EsConstant.CATEGORY_INCLUDE));
        }
    }

    /**
     * 关键字搜索
     */
    private void keywordSearch(EsProductParam param, BoolQueryBuilder boolQueryBuilder, Integer lang) {
        boolean keywordNotBlank = StrUtil.isNotBlank(param.getKeyword());
        if (!keywordNotBlank && StrUtil.isBlank(param.getShopName())) {
            return;
        }
        // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可
        BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery();
        // 商家、平台端商品名称模糊搜索
        if (!BooleanUtil.isTrue(param.getAppDisplay()) && keywordNotBlank) {
            BoolQueryBuilder wildcardBoolQuery = QueryBuilders.boolQuery()
                    .must(QueryBuilders.wildcardQuery(EsConstant.LANG_UNION_PROD_NAME_LIKE, "*" + param.getKeyword() + "*"))
                    .must(QueryBuilders.termQuery(EsConstant.LANG_UNION_LANG, lang));
            keywordShouldQuery.must(QueryBuilders.nestedQuery(
                    EsConstant.PROD_LANG_LIST,
                    wildcardBoolQuery,
                    ScoreMode.None).boost(10)
            );
        }
        // 用户端商品名称分词搜索
        else if (keywordNotBlank){
            // 根据当前语言和关键词查询商品
            BoolQueryBuilder ikBoolQuery = QueryBuilders.boolQuery()
                    .must(QueryBuilders.matchQuery(EsConstant.LANG_UNION_PROD_NAME, param.getKeyword()))
                    .must(QueryBuilders.termQuery(EsConstant.LANG_UNION_LANG, lang));

            keywordShouldQuery.should(QueryBuilders.nestedQuery(
                    EsConstant.PROD_LANG_LIST,
                    ikBoolQuery,
                    ScoreMode.None).boost(10)
            );
            BoolQueryBuilder ngramBoolQuery = QueryBuilders.boolQuery()
                    .must(QueryBuilders.termQuery(EsConstant.LANG_UNION_PROD_NAME_NGRAM, param.getKeyword()))
                    .must(QueryBuilders.termQuery(EsConstant.LANG_UNION_LANG, lang));

            keywordShouldQuery.should(QueryBuilders.nestedQuery(
                    EsConstant.PROD_LANG_LIST,
                    ngramBoolQuery,
                    ScoreMode.None).boost(10)
            );

        }
        // 店铺名称关键词搜索
        if (StrUtil.isNotBlank(param.getShopName())) {
            keywordShouldQuery.must(QueryBuilders.wildcardQuery(EsConstant.SHOP_NAME, "*" + param.getShopName() + "*").boost(10));
        }

        boolQueryBuilder.must(keywordShouldQuery);
    }

    /**
     * 进行排序
     */
    private void sort(EsProductParam param, SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {

        // 用户端、商家端、平台端自定义排序
        if (Objects.nonNull(param.getSort())) {
            if (Objects.equals(param.getSort(), EsProductSortEnum.NON_SORT.value())) {
                // 不参与任何排序
                searchSourceBuilder.query(boolQueryBuilder);
                return;
            }
            for (EsProductSortEnum enumValue : EsProductSortEnum.values()) {
                if (!Objects.equals(enumValue.value(), param.getSort())) {
                    continue;
                }
                searchSourceBuilder.sort(enumValue.param(), enumValue.order());
            }
            //封装所有的查询条件(没有function score)
            searchSourceBuilder.query(boolQueryBuilder);
            return;
        }

        if (Objects.equals(param.getShowSpuType(),1)) {
            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder).boostMode(CombineFunction.SUM);
            searchSourceBuilder.query(functionScoreQueryBuilder);
            return;
        }

        // 优先店铺名称、商品名称搜索排序
        if (StrUtil.isNotBlank(param.getKeyword()) || StrUtil.isNotBlank(param.getShopName())) {
            // 关键字排序
            keywordSort(param, searchSourceBuilder, boolQueryBuilder);
            return;
        }

        // 默认排序
        if (Objects.nonNull(param.getShopId()) && !BooleanUtil.isTrue(param.getAppDisplay())) {
            // 商家端默认排序 - 优先显示序号大的商品
            searchSourceBuilder.sort(EsProductSortEnum.SEQ_DESC.param(), EsProductSortEnum.SEQ_DESC.order());
        } else {
            // 平台端、用户端默认排序 - 优先置顶的商品, 再到销量（实际销量+注水销量）
            searchSourceBuilder.sort(EsProductSortEnum.IS_TOP_DESC.param(), EsProductSortEnum.IS_TOP_DESC.order());
            searchSourceBuilder.sort(EsProductSortEnum.SALE_NUM_DESC.param(), EsProductSortEnum.SALE_NUM_DESC.order());
        }
        searchSourceBuilder.sort(EsProductSortEnum.CREATE_TIME_DESC.param(), EsProductSortEnum.CREATE_TIME_DESC.order());
        searchSourceBuilder.sort(EsProductSortEnum.PROD_ID_DESC.param(), EsProductSortEnum.PROD_ID_DESC.order());
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 关键字搜索排序
     * @param param
     * @param searchSourceBuilder
     * @param boolQueryBuilder
     */
    private void keywordSort(EsProductParam param, SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
        boolean keywordNotBlank = StrUtil.isNotBlank(param.getKeyword());
        if (keywordNotBlank) {
            if (BooleanUtil.isTrue(param.getAppDisplay())) {
                List<String> resultList = new ArrayList<>();

                try {
                    IKSegmenter ik = new IKSegmenter(new StringReader(param.getKeyword()), false);
                    Lexeme lexeme;
                    while((lexeme = ik.next()) != null) {
                        resultList.add(lexeme.getLexemeText());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                for (String result : resultList) {
                    FunctionScoreQueryBuilder.FilterFunctionBuilder prodNameScoreIk = new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                            QueryBuilders.nestedQuery(EsConstant.PROD_LANG_LIST,
                                    QueryBuilders.boolQuery().must(QueryBuilders.matchPhraseQuery(EsConstant.LANG_UNION_PROD_NAME,  result)), ScoreMode.None),
                            ScoreFunctionBuilders.weightFactorFunction(5));
                    filterFunctionBuilders.add(prodNameScoreIk);
                }
                String replace = param.getKeyword().replaceAll("[a-zA-Z0-9\\s]", "");
                if (replace.length() > 10) {
                    replace = replace.substring(10);
                }
                String[] keywordArray = replace.split("");
                for (String value : keywordArray) {
                    if (StrUtil.isBlank(value)) {
                        continue;
                    }
                    FunctionScoreQueryBuilder.FilterFunctionBuilder prodNameScoreNgram = new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                            QueryBuilders.nestedQuery(EsConstant.PROD_LANG_LIST,
                                    QueryBuilders.boolQuery().must(QueryBuilders.matchPhraseQuery(EsConstant.LANG_UNION_PROD_NAME_NGRAM,  value)), ScoreMode.None),
                            ScoreFunctionBuilders.weightFactorFunction(1));
                    filterFunctionBuilders.add(prodNameScoreNgram);
                }

                // 商品名称分词查询排序
                // 优先商品名称排序
                FunctionScoreQueryBuilder.FilterFunctionBuilder prodNameScore = new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.nestedQuery(EsConstant.PROD_LANG_LIST,
                                        QueryBuilders.boolQuery().must(QueryBuilders.termQuery(EsConstant.LANG_UNION_PROD_NAME_KEYWORD,  param.getKeyword())), ScoreMode.None),
                                ScoreFunctionBuilders.weightFactorFunction(100));
                filterFunctionBuilders.add(prodNameScore);

                searchSourceBuilder.sort(SortBuilders.scoreSort().order(SortOrder.DESC));
                searchSourceBuilder.sort(EsProductSortEnum.SALE_NUM_DESC.param(), EsProductSortEnum.SALE_NUM_DESC.order());

            }

            else {
                // 商品名称模糊查询排序
                // 优先商品名称完全匹配
                filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.nestedQuery(
                                EsConstant.PROD_LANG_LIST,
                                QueryBuilders.wildcardQuery(EsConstant.LANG_UNION_PROD_NAME_LIKE, param.getKeyword()),
                                ScoreMode.None),
                        ScoreFunctionBuilders.weightFactorFunction(200)
                ));
                // 商品名称前缀匹配
                filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.nestedQuery(
                                EsConstant.PROD_LANG_LIST,
                                QueryBuilders.wildcardQuery(EsConstant.LANG_UNION_PROD_NAME_LIKE, param.getKeyword() + "*"),
                                ScoreMode.None),
                        ScoreFunctionBuilders.weightFactorFunction(100)
                ));
            }
        }

        if (StrUtil.isNotBlank(param.getShopName())) {
            // 店铺名称完全匹配
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                    QueryBuilders.wildcardQuery(EsConstant.SHOP_NAME, param.getShopName()),
                    ScoreFunctionBuilders.weightFactorFunction(400)
            ));
            // 店铺名称前缀匹配
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                    QueryBuilders.wildcardQuery(EsConstant.SHOP_NAME, param.getShopName() + "*"),
                    ScoreFunctionBuilders.weightFactorFunction(200)
            ));
        }

        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder, ArrayUtil.toArray(filterFunctionBuilders, FunctionScoreQueryBuilder.FilterFunctionBuilder.class))
                .scoreMode(FunctionScoreQuery.ScoreMode.SUM).boostMode(CombineFunction.SUM);
        // 封装所有的查询条件(带有function score)
        searchSourceBuilder.query(functionScoreQueryBuilder);
    }

    /**
     * 过滤查询条件，如果有必要的话
     * @param param 查询条件
     * @param boolQueryBuilder 组合进boolQueryBuilder
     */
    private void filterQueryIfNecessary(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {

        // 用户端搜索
        if(param.getAppDisplay()) {
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.APP_DISPLAY, param.getAppDisplay()));
        }
        // 商品（状态、库存、商品类型、商品id、商品id列表, 组合商品,配送方式,是否置顶）
        spuFilterQuery(param, boolQueryBuilder);

        // 分类（商家分类，平台一二三级分类）
        categoryFilterQuery(param, boolQueryBuilder);

        // 活动 （活动商品id）
        activityFilterQuery(param, boolQueryBuilder);

        // 商品扩展信息筛选（店铺id、店铺类型、品牌）
        extensionFilterQuery(param, boolQueryBuilder);

        // 范围筛选（价格、销量）
        rangeFilterQuery(param, boolQueryBuilder);
    }

    /**
     * 范围过滤
     * @param param 查询条件
     * @param boolQueryBuilder
     */
    private void rangeFilterQuery(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {
        // 价格区间
        if(param.getMinPrice() != null || param.getMaxPrice() != null){
            boolQueryBuilder.filter(EsSearchUtil.rangeQuery(EsConstant.PRICE,param.getMinPrice(), param.getMaxPrice()));
        }
        // 销量区间
        if(param.getMinSaleNum() != null || param.getMaxSaleNum() != null){
            boolQueryBuilder.filter(EsSearchUtil.rangeQuery(param.getAppDisplay() ? EsConstant.SOLD_NUM : EsConstant.ACTUAL_SOLD_NUM,
                    param.getMinSaleNum(), param.getMaxSaleNum()));
        }
    }

    /**
     * 商品扩展信息过滤
     * @param param 查询条件
     * @param boolQueryBuilder
     */
    private void extensionFilterQuery(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {
        // 店铺id
        if(Objects.nonNull(param.getShopId())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_ID, param.getShopId()));
        }

        // 店铺类型
        if(Objects.nonNull(param.getShopType())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_TYPE, param.getShopType()));
        }
       // 供应商
        if(Objects.nonNull(param.getIsSupplier())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_SUPPLIER, param.getIsSupplier()));
        }

        // 品牌
        if(StrUtil.isNotBlank(param.getBrandIds())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.BRAND_ID,  param.getBrandIds()));
        }

    }

    /**
     * 商品活动信息过滤
     * @param param 查询条件
     * @param boolQueryBuilder
     */
    private void activityFilterQuery(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {
        // 商品活动Id
        if(Objects.nonNull(param.getActivityId())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.ACTIVITY_ID, param.getActivityId()));
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.PROD_TYPE, param.getProdType()));
        }
        if (Objects.equals(param.getProdType(), ProdType.PROD_TYPE_GROUP.value()) && param.getAppDisplay()){
            boolQueryBuilder.filter(EsSearchUtil.rangeQuery(EsConstant.ACTIVITY_START_TIME, null, System.currentTimeMillis()));
        } else if (Objects.equals(param.getProdType(), ProdType.PROD_TYPE_SECKILL.value()) && param.getAppDisplay()){
            boolQueryBuilder.must(QueryBuilders.existsQuery(EsConstant.ACTIVITY_START_TIME));
        }
    }

    /**
     * 商品信息过滤
     * @param param 查询条件
     * @param boolQueryBuilder
     */
    private void spuFilterQuery(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {
        // spu状态
        List<Integer> statusList = new ArrayList<>();
        if (Objects.nonNull(param.getStatus())) {
            statusList.add(param.getStatus());
        }
        // 装修以及定时任务获取删除商品
        else if (!BooleanUtil.isTrue(param.getGetDelete())) {
            statusList.add(StatusEnum.ENABLE.value());
            statusList.add(StatusEnum.DISABLE.value());
            statusList.add(StatusEnum.OFFLINE.value());
            statusList.add(StatusEnum.WAIT_AUDIT.value());
            statusList.add(ProdStatusEnums.AUDIT.getValue());
        }
        if(CollUtil.isNotEmpty(statusList)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.STATUS, statusList));
        }
        // 是否有库存
        if(Objects.nonNull(param.getHasStock())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.HAS_STOCK, BooleanUtil.isTrue(param.getHasStock())));
        }
        // 商品类型
        if(Objects.nonNull(param.getProdType())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.PROD_TYPE, param.getProdType()));
        }
        // 不匹配的商品类型
        if(CollUtil.isNotEmpty(param.getMustNotProdTypes())){
            for (Integer prodType : param.getMustNotProdTypes()) {
                boolQueryBuilder.mustNot(QueryBuilders.termQuery(EsConstant.PROD_TYPE, prodType.toString()));
            }
        }
        // 是否过滤活动商品
        if(Objects.nonNull(param.getIsActive()) && param.getIsActive() == 1){
            boolQueryBuilder.mustNot(QueryBuilders.termQuery(EsConstant.PROD_TYPE, ProdType.PROD_TYPE_ACTIVE.value()));
        }
        // 商品类别
        if(Objects.nonNull(param.getMold())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.MOLD, param.getMold().toString()));
        }
        // 商品类别
        if(Objects.nonNull(param.getNotMold())){
            boolQueryBuilder.mustNot(QueryBuilders.termQuery(EsConstant.MOLD, param.getNotMold().toString()));
        }
        // spuId
        if(Objects.nonNull(param.getProdId())){
            boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.PROD_ID,param.getProdId().toString()));
        }
        // spuId列表
        else if(CollectionUtil.isNotEmpty(param.getProdIds())){
            boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.PROD_ID,param.getProdIds()));
        }

        // 不匹配的商品列表
        if(CollUtil.isNotEmpty(param.getExcludesProdIds())){
            for (Long prodId : param.getExcludesProdIds()) {
                boolQueryBuilder.mustNot(QueryBuilders.termQuery(EsConstant.PROD_ID, prodId.toString()));
            }
        }
        // 查询不在该集合中的商品
        if(Objects.nonNull(param.getSpuIdsExclude())){
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery(EsConstant.PROD_ID,param.getSpuIdsExclude()));
        }
        // 配送方式
        if(Objects.nonNull(param.getDeliveryMode())){
            boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.DELIVERIES,param.getDeliveryMode().toString()));
        }
        // 是否置顶
        if(Objects.nonNull(param.getIsTop())){
            boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.IS_TOP,param.getIsTop().toString()));
        }
    }

    /**
     * 商品分类信息过滤
     * @param param
     * @param boolQueryBuilder
     */
    private void categoryFilterQuery(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {
        //商家分类
        if(Objects.nonNull(param.getShopCategoryId())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_CATEGORY_ID, param.getShopCategoryId()));
        }

        // 平台一级分类
        if(Objects.nonNull(param.getPrimaryCategoryId())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.PRIMARY_CATEGORY_ID, param.getPrimaryCategoryId()));
        }

        // 查询不在该集合中的商品
        if(Objects.nonNull(param.getNotPrimaryCategoryId())) {
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery(EsConstant.PRIMARY_CATEGORY_ID, param.getNotPrimaryCategoryId().toString()));
        }

        // 平台二级分类
        if(Objects.nonNull(param.getSecondaryCategoryId())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SECONDARY_CATEGORY_ID, param.getSecondaryCategoryId()));
        }

        // 平台三级分类
        if(Objects.nonNull(param.getCategoryId())){
            boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.CATEGORY_ID, param.getCategoryId()));
        }
        // 平台三级分类
        if(CollectionUtil.isNotEmpty(param.getCategoryIds())){
            boolQueryBuilder.filter(EsSearchUtil.nestedQuery(EsConstant.CATEGORY, EsConstant.CATEGORY_UNION_ID, param.getCategoryIds()));
        }
    }

    /**
     * 基础分页
     * @param pageParam 分页参数
     * @param productParam 查询参数
     * @param isAgg 是否聚合
     * @return 分页数据
     */
    private EsPageVO<ProductSearchVO> basePage(EsPageParam pageParam, EsProductParam productParam, Boolean isAgg) {
        if (esProperties.getEnable()) {
            return this.basePageByEs(pageParam, productParam, isAgg);
        } else {
            return this.basePageByDb(pageParam, productParam);
        }
    }

    /**
     * 基础分页(通过es)
     * @param pageParam 分页查询
     * @param productParam 查询参数
     * @param isAgg 是否聚合
     * @return 分页数据
     */
    private EsPageVO<ProductSearchVO> basePageByEs(EsPageParam pageParam, EsProductParam productParam, Boolean isAgg) {
        EsPageVO<ProductSearchVO> result = new EsPageVO<>();
        SearchResponse response = pageSearchResult(pageParam, productParam, isAgg);
        // 商品信息
        result.setRecords(SearchResponseUtil.buildSpuSearchList(response.getHits()));
        // 分页信息
        SearchResponseUtil.buildSearchPage(pageParam, result, response);
        return result;
    }

    /**
     * 基础分页(通过数据库)
     * @param esPageParam 分页参数
     * @param productParam 查询参数
     * @return 分页数据
     */
    private EsPageVO<ProductSearchVO> basePageByDb(EsPageParam esPageParam, EsProductParam productParam) {
        PageParam<ProductSearchVO> pageParam = new PageParam<>();
        pageParam.setCurrent(esPageParam.getCurrent());
        pageParam.setSize(esPageParam.getSize());
        this.handleProdParam(productParam);
        Page<ProductSearchVO> page = esProductMapper.pageEsProduct(pageParam, productParam);
        this.fillEsProductInfo(productParam, page.getRecords());
        // 封装成esPage
        EsPageVO<ProductSearchVO> esPage = new EsPageVO<>();
        esPage.setPages((int)pageParam.getPages());
        esPage.setTotal(pageParam.getTotal());
        esPage.setRecords(page.getRecords());
        return esPage;
    }

    /**
     * 处理es商品查询参数,方便数据库查询
     * @param productParam 查询参数
     */
    private void handleProdParam(EsProductParam productParam) {
        // 补充匹配参数
        if (Objects.isNull(productParam.getFetchSource())) {
            productParam.setFetchSource(EsConstant.APP_FETCH_SOURCE);
        }
        // 查询活动信息
        productParam.setActivityQuery(this.isFill(productParam, EsConstant.ACTIVITY_PRICE));
        // 分割品牌id字符串
        if (!ObjectUtils.isEmpty(productParam.getBrandIds())) {
            List<Long> brandIds = Arrays.stream(productParam.getBrandIds().split(",")).map(Long::valueOf).toList();
            productParam.setBrandIdList(brandIds);
        }
        //
        if(productParam.getLang() == null){
            productParam.setLang(I18nMessage.getLang());
        }
        //排序顺序
        if (!Objects.isNull(productParam.getSort())
                && !Objects.equals(productParam.getSort(), EsProductSortEnum.NON_SORT.value())) {
            EsProductSortEnum sortEnum = EsProductSortEnum.instance(productParam.getSort());
            if (!Objects.isNull(sortEnum)) {
                productParam.setSortParamName(sortEnum.param());
                productParam.setSortOrder(sortEnum.order().toString());
            }
        }
    }

    /**
     * 补充es商品信息(从数据库中查出来的)
     * @param products 商品集合
     */
    private void fillEsProductInfo(EsProductParam productParam, List<ProductSearchVO> products) {
        if (CollectionUtils.isEmpty(products)) {
            return;
        }
        List<Long> prodIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (ProductSearchVO product : products) {
            prodIds.add(product.getProdId());
        }
        // 数据准备 - 多语言
        Map<Long, List<ProductSearchLangVO>> prodLangMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (this.isFill(productParam, EsConstant.PROD_LANG_LIST)) {
            List<ProdLang> prodLangs = prodLangService.list(new LambdaQueryWrapper<ProdLang>().in(ProdLang::getProdId, prodIds));
            if (!CollectionUtils.isEmpty(prodLangs)) {
                prodLangMap = prodLangs.stream().map(prodLang -> {
                    ProductSearchLangVO productSearchLangVO = new ProductSearchLangVO();
                    BeanUtils.copyProperties(prodLang, productSearchLangVO);
                    return productSearchLangVO;
                }).collect(Collectors.groupingBy(ProductSearchLangVO::getProdId));
            }
        }
        // 数据补充
        for (ProductSearchVO product : products) {
            // 多语言
            product.setProdLangList(prodLangMap.get(product.getProdId()));
            // 销量
            if (this.isFill(productParam, EsConstant.SOLD_NUM)) {
                product.setSoldNum(product.getActualSoldNum() + product.getWaterSoldNum());
            }
        }
    }

    /**
     * 判断是否需要补充信息
     * @param productParam 查询参数
     * @param param 匹配参数
     * @return true/false
     */
    private boolean isFill(EsProductParam productParam, String param) {
        if (Objects.isNull(productParam.getFetchSource())) {
            return true;
        }
        for (String fetchParam : productParam.getFetchSource()) {
            if (Objects.equals(param, fetchParam)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 构建EsProductSearchVO
     * @param products 商品集合
     * @param isAgg 是否聚合
     * @return EsProductSearchVO
     */
    private EsProductSearchVO getEsProdSearchVO(List<ProductSearchVO> products, Boolean isAgg) {
        EsProductSearchVO esProductSearchVO = new EsProductSearchVO();
        esProductSearchVO.setProducts(products);
        if (!BooleanUtil.isTrue(isAgg) || CollectionUtils.isEmpty(products)) {
            return esProductSearchVO;
        }
        Set<Long> categoryIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> brandIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (ProductSearchVO product : products) {
            categoryIds.add(product.getCategoryId());
            if (!Objects.isNull(product.getBrandId())) {
                brandIds.add(product.getBrandId());
            }
        }
        // 补充三级分类集合
        List<CategorySearchVO> categories = categoryService.listEsCategoryByCategoryIds(new ArrayList<>(categoryIds));
        esProductSearchVO.setCategories(categories);
        // 补充品牌集合
        List<BrandSearchVO> brands = brandService.listEsBrandByBrandIds(new ArrayList<>(brandIds));
        esProductSearchVO.setBrands(brands);
        // 补充店铺信息(暂不补充, 有点意义不明)
        return esProductSearchVO;
    }
}
